<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */
namespace proxy;

use app\enum\CodeEnum;

class Curl implements Base
{
    /**
     * @var false|resource curl实例
     */
    protected $handle;

    /**
     * GET请求.
     *
     * @throws \Exception
     */
    public function get(string $serviceName = '', string $url = '', array $data = []): bool
    {
        $host = $this->getServiceHost($serviceName);
        $url = $host . $url;
        $args = [];
        foreach ($data as $key => $value) {
            $args[$key] = is_string($value) ? $value : json_encode($value);
        }
        if (strpos($url, '?') !== false) {
            $url .= '&' . http_build_query($args);
        } else {
            $url .= '?' . http_build_query($args);
        }
        return $this->setUrl($url);
    }

    /**
     * GET JSON请求.
     *
     * @throws \Exception
     */
    public function getJson(string $serviceName = '', string $url = '', array $data = []): bool
    {
        $host = $this->getServiceHost($serviceName);
        $url = $host . $url;
        curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, 'GET');
        curl_setopt($this->handle, CURLOPT_POSTFIELDS, json_encode($data));
        return $this->setUrl($url);
    }

    /**
     * @throws \Exception
     */
    public function post(string $serviceName = '', string $url = '', array $data = []): bool
    {
        $host = $this->getServiceHost($serviceName);
        $url = $host . $url;
        curl_setopt($this->handle, CURLOPT_POST, true);
        curl_setopt($this->handle, CURLOPT_POSTFIELDS, http_build_query($data));
        return $this->setUrl($url);
    }

    /**
     * POST JSON请求.
     *
     * @throws \Exception
     */
    public function postJson(string $serviceName = '', string $url = '', array $data = []): bool
    {
        $host = $this->getServiceHost($serviceName);
        $url = $host . $url;
        curl_setopt($this->handle, CURLOPT_POST, true);
        curl_setopt($this->handle, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
        return $this->setUrl($url);
    }

    /**
     * 设置请求头.
     */
    public function setHeader(array $headers = []): bool
    {
        $items = [];
        if ($headers) {
            foreach ($headers as $k => $v) {
                $items[] = $k . ': ' . $v;
            }
        }
        return curl_setopt($this->handle, CURLOPT_HTTPHEADER, $items);
    }

    /**
     * 设置调用超时时间.
     *
     * @param mixed $timeout
     */
    public function setTimeout($timeout = 30): bool
    {
        // 设置连接超时与请求超时
        return curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, $timeout) && curl_setopt($this->handle, CURLOPT_TIMEOUT, $timeout);
    }

    /**
     * 获取服务地址.
     *
     * @param string $name 服务名
     * @throws \Exception
     */
    public function getServiceHost(string $name = '')
    {
        if (! $name) {
            throw new \Exception('未知服务');
        }
        $host = env('base_url.' . $name, '');
        if (! $host) {
            throw new \Exception('获取服务地址失败');
        }
        return $host;
    }

    /**
     * 创建handle.
     *
     * @return $this
     */
    public function setHandle(): Curl
    {
        $this->handle = curl_init();
        curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->handle, CURLOPT_MAXREDIRS, 10);
        curl_setopt($this->handle, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, true);
        return $this;
    }

    /**
     * 设置请求连接.
     */
    public function setUrl(string $url = ''): bool
    {
        curl_setopt($this->handle, CURLOPT_URL, $url);
        if (strpos($url, 'https://') === 0) {
            curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($this->handle, CURLOPT_SSLVERSION, 6);
        }
        return true;
    }

    /**
     * 请求数据.
     *
     * @return bool|string
     * @throws \Exception
     */
    public function exec()
    {
        $res = curl_exec($this->handle);
        if (curl_errno($this->handle)) {
            throw new \Exception(curl_error($this->handle));
        }
        $this->status();
        curl_close($this->handle);
        return $res;
    }

    /**
     * 应答状态检查.
     *
     * @throws \Exception
     */
    public function status(): bool
    {
        $status = curl_getinfo($this->handle, CURLINFO_HTTP_CODE);
        if ($status == CodeEnum::SUCCESS) {
            return true;
        }
        if ($status == CodeEnum::NOT_FOUND_ERROR) {
            throw new \Exception(CodeEnum::CODE_MESSAGE[CodeEnum::NOT_FOUND_ERROR]);
        }
        if ($status == CodeEnum::SERVER_ERROR) {
            throw new \Exception(CodeEnum::CODE_MESSAGE[CodeEnum::SERVER_ERROR]);
        }
        return true;
    }
}
